package org.simplextensions.scanner;

import javassist.bytecode.ClassFile;
import org.simplextensions.registry.exceptions.ExtensionsConfigurationException;

import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;

public abstract class ClassFileIterator implements Iterator<ClassFile> {

	private Set<String> classNamesFilter;

	private ClassFile next;

	private final ClassScannerBundle csb;

	public ClassFileIterator(ClassScannerBundle csb) {
		this.csb = csb;
	}

	public ClassScannerBundle getCSB() {
		return this.csb;
	}

	public boolean hasNext() {
		if (next != null) {
			return true;
		} else {

			do {
				next = searchNext();

				if (next == null) {
					if (getCSB().getClassNames().size() > 0 && getClassNamesFilter().size() > 0)
						throw new ExtensionsConfigurationException("Bundle " + getCSB().getName()
								+ " defines classes not available in classpath: " + getClassNamesFilter().iterator().next());
				}
			} while (next != null && (getCSB().getClassNames().size() > 0 && !getClassNamesFilter().contains(next.getName())));
			if (next != null)
				classVisited(next.getName());
			return next != null;
		}
	}

	public ClassFile next() {
		if (hasNext()) {
			try {
				return next;
			} finally {
				next = null;
			}
		}
		throw new NoSuchElementException();
	}

	protected abstract ClassFile searchNext();

	public final void remove() {
		throw new UnsupportedOperationException("remove not supported");
	}

	public void classVisited(String name) {
		getClassNamesFilter().remove(name);
	}

	public Set<String> getClassNamesFilter() {
		if (classNamesFilter == null) {
			classNamesFilter = new HashSet<String>();
			if (getCSB().getClassNames() != null)
				classNamesFilter.addAll(getCSB().getClassNames());
		}
		return classNamesFilter;
	}
}